package com.hsmpay.mobile.util.icc;

public final class ByteUtil {
    static char[] encode2Base64Chars(byte[] data) {
        char[] out = new char[((data.length + 2) / 3) * 4];

        //
        // 3 bytes encode to 4 chars. Output is always an even
        // multiple of 4 characters.
        //
        for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
            boolean quad = false;
            boolean trip = false;

            int val = (0xFF & (int) data[i]);
            val <<= 8;
            if ((i + 1) < data.length) {
                val |= (0xFF & (int) data[i + 1]);
                trip = true;
            }
            val <<= 8;
            if ((i + 2) < data.length) {
                val |= (0xFF & (int) data[i + 2]);
                quad = true;
            }
            out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
            val >>= 6;
            out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
            val >>= 6;
            out[index + 1] = alphabet[val & 0x3F];
            val >>= 6;
            out[index + 0] = alphabet[val & 0x3F];
        }
        return out;
    }

    static String encodeToBase64String(byte[] data) {
        return new String(encode2Base64Chars(data));
    }

    static byte[] decodeBase64(char[] data) {

        int tempLen = data.length;
        for (int ix = 0; ix < data.length; ix++) {
            if ((data[ix] > 255) || codes[data[ix]] < 0)
                --tempLen;
        }

        int len = (tempLen / 4) * 3;
        if ((tempLen % 4) == 3)
            len += 2;
        if ((tempLen % 4) == 2)
            len += 1;

        byte[] out = new byte[len];

        int shift = 0;
        int accum = 0;
        int index = 0;

        for (int ix = 0; ix < data.length; ix++) {
            int value = (data[ix] > 255) ? -1 : codes[data[ix]];

            if (value >= 0) {
                accum <<= 6;
                shift += 6;
                accum |= value;
                if (shift >= 8) {
                    shift -= 8;
                    out[index++] = (byte) ((accum >> shift) & 0xff);
                }
            }
        }

        if (index != out.length) {
            throw new Error("Miscalculated data length (wrote " + index
                    + " instead of " + out.length + ")");
        }

        return out;
    }

    static byte[] decodeBase64(String data) {
        return decodeBase64(data.toCharArray());
    }

    public static String toHexString(byte[] b) {
        StringBuilder sb = new StringBuilder(b.length * 2);
        for (int i = 0; i < b.length; i++) {
            sb.append(HEXCHAR[(b[i] & 0xf0) >>> 4]);
            sb.append(HEXCHAR[b[i] & 0x0f]);
        }
        return sb.toString();
    }

    public static final byte[] toBytes(String s) {
        byte[] bytes;
        bytes = new byte[s.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) Integer.parseInt(s.substring(2 * i, 2 * i + 2),
                    16);
        }
        return bytes;
    }


    public static byte[] hex2Bin(String hex) {
        byte[] hexbin = hex.getBytes();
        byte[] data = new byte[(hexbin.length + 1) / 2];

        for (int i = 0; i < hexbin.length; i++) {
            int v = Math.abs(hexbin[i]);
            if (v >= '0' && v <= '9')
                v = v - '0';
            else if (v >= 'A' && v <= 'F')
                v = v - 'A' + 10;
            else if (v >= 'a' && v <= 'f')
                v = v - 'a' + 10;
            else
                v = 0;
            v &= 0x0f;
            if (i % 2 == 0) {
                v <<= 4;
            }
            data[i / 2] |= v;
        }
        return data;

    }

    public static void shiftLeft(byte[] bytes, int bits) {
        byte lastShift = 0;
        byte newShift = 0;
        for (int i = 0; i < bytes.length; i++) {
            newShift = bytes[i];
            newShift >>>= (8 - bits);
            bytes[i] <<= bits;
            bytes[i] |= lastShift;
            lastShift = newShift;
        }
    }

    public static void shiftRight(byte[] bytes, int bits) {
        int lastShift = 0;
        int newShift = 0;
        for (int i = 0; i < bytes.length; i++) {
            int v = Math.abs(bytes[i]);
            newShift = v;
            newShift <<= (8 - bits);
            newShift &= 0xff;

            v >>>= bits;
            v |= lastShift;
            lastShift = newShift;
            bytes[i] = (byte) (v & 0xff);
        }
    }


    public static void printBytesHex(String title, byte[] bytes) {
        System.out.println(title);

        for (int i = 0; i < bytes.length; i++) {
            System.out.printf("%02X", bytes[i]);
        }
        System.out.println();

    }

    public static void printBytesBin(String title, byte[] bytes) {
        System.out.println(title);

        for (int i = 0; i < bytes.length; i++) {
            int v = bytes[i];
            int mask = (int) 0x80;

            for (int j = 0; j < 8; j++) {
                if ((mask & v) != 0)
                    System.out.print("1");
                else
                    System.out.print("0");
                mask >>>= 1;
            }
        }
        System.out.println();
    }

    private static char[] HEXCHAR = {'0', '1', '2', '3', '4', '5', '6', '7',
            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

    static private char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
            .toCharArray();

    static private byte[] codes = new byte[256];

    static {
        for (int i = 0; i < 256; i++)
            codes[i] = -1;
        for (int i = 'A'; i <= 'Z'; i++)
            codes[i] = (byte) (i - 'A');
        for (int i = 'a'; i <= 'z'; i++)
            codes[i] = (byte) (26 + i - 'a');
        for (int i = '0'; i <= '9'; i++)
            codes[i] = (byte) (52 + i - '0');
        codes['+'] = 62;
        codes['/'] = 63;
    }

}
